package svckit;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpProgressMonitor;

public class ExeUtil extends Thread
{
	static public class ExeResult
	{

		public String output;
		public int exitCode;
	}
	
	static public class SSHClient
	{
		public String host;
		public int port;
		public String user;
		public String password;
		
		public SSHClient(String host,String user,String password)
		{
			this.host=host;
			this.port=22;
			this.user=user;
			this.password=password;
		}
		
		public SSHClient(String host,int port,String user,String password)
		{
			this.host=host;
			this.port=port;
			this.user=user;
			this.password=password;
		}
	}
	
	static public class Task
	{
		public String name;
		public String cmd;
		public long time;
		public long timeout;
		
		public String toString()
		{
			if(name!=null)
				return name+"_"+time;
			return cmd+"_"+time;
		}
	}
	

	static ExeUtil instance=null;
    static final Process[] emptyProcessArray=new Process[0];
	
	//默认命令超时参数
	static public final long DefaultCmdTimeout=5000l;
	//默认SSH命令超时参数
	static public final long DefaultSSHCmdTimeout=60000l;
	//初始字节数组大小
	static public final int InitBytesSize=1024;

	static public final Charset charset=Charset.forName(System.getProperty("sun.jnu.encoding"));
	
	static String readString(InputStream is) throws IOException
	{
		byte[] data=new byte[InitBytesSize];
		int i=0,b;
		while((b=is.read())!=-1)
		{
			data[i++]=(byte)b;
			if(i>=data.length) data=Arrays.copyOf(data, data.length*2);	
		}
		return new String(data,0,i,charset);
	}
	

	static public ExeResult run(String name,File dir,String[] env,long timeout,String ...cmd)
	{
		ExeResult result=new ExeResult();
		try{
			
			Process process;
			if(cmd.length==1)
				process=Runtime.getRuntime().exec(cmd[0],env,dir);
			else
				process=Runtime.getRuntime().exec(cmd,env,dir);
			if(timeout>0l)
				 addTask(name,cmd[0],process,timeout);
			 result.output=readString(process.getInputStream());
			 result.output+=readString(process.getErrorStream());
			 result.exitCode=process.waitFor();
		}
		catch(Exception e)
		{
			result.exitCode=-1;
			result.output+=e.toString();
		}
		return result;
	}
	
	static public Task runAsync(String name,File dir,String[] env,long timeout,String ...cmd)
	{
		try{
			
			Process process;
			if(cmd.length==1)
				process=Runtime.getRuntime().exec(cmd[0],env,dir);
			else
				process=Runtime.getRuntime().exec(cmd,env,dir);
			return addTask(name,cmd[0],process,timeout);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
	
	static public String run(String cmd)
	{
		ExeResult result=run(null,null,null,0l,cmd);
		return result.output;
	}
	
	static public String run(String cmd,long timeout)
	{
		ExeResult result=run(null,null,null,timeout,cmd);
		return result.output;
	}
	
	static public String sshRun(SSHClient client,String cmd,long timeout)
	{
		try
		{
			JSch jsch=new JSch();
//			if(client.identity!=null&&!client.identity.isEmpty())
//				jsch.addIdentity(client.identity);
			Session session=jsch.getSession(client.user, client.host, client.port);
	        session.setConfig("StrictHostKeyChecking", "no");
	        //set auth info interactively
	        //session.setUserInfo(new UserInfo(){.....});
	        session.setPassword(client.password);
	        session.connect();
	        
	        com.jcraft.jsch.ChannelExec ec=(com.jcraft.jsch.ChannelExec)session.openChannel("exec");
	        ec.setCommand(cmd);
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        ec.setInputStream(null);
	        ec.setErrStream(out);
	        ec.setOutputStream(out);
	        ec.connect();
	        long time=System.currentTimeMillis();
	        while(!ec.isClosed())
	        {
	            Thread.sleep(200);
	            if(System.currentTimeMillis()-time>=timeout)
	            {
	            	out.write("\nError:user ssh timeout".getBytes());
	        		break;
	            }
	        }
	        ec.disconnect();
	        session.disconnect();
	        return out.toString(charset.name());
		}
		catch(Exception e)
		{
			return e.getMessage();
		}
	}
	
	
	static public boolean sshUpload(SSHClient client,String src,String dest)
	{
		try
		{
			JSch jsch=new JSch();
//			if(client.identity!=null&&!client.identity.isEmpty())
//				jsch.addIdentity(client.identity);
			Session session=jsch.getSession(client.user, client.host, client.port);
	        session.setConfig("StrictHostKeyChecking", "no");
	        //set auth info interactively
	        //session.setUserInfo(new UserInfo(){.....});
	        session.setPassword(client.password);
	        session.connect();
	        com.jcraft.jsch.ChannelSftp fc=(com.jcraft.jsch.ChannelSftp)session.openChannel("sftp");
	        fc.connect();
	        fc.put(src,dest);
	        fc.disconnect();
	        session.disconnect();
	        return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return false;
	}
	
	static public boolean sshDownload(SSHClient client,String src,String dest)
	{
		try
		{
			JSch jsch=new JSch();
//			if(client.identity!=null&&!client.identity.isEmpty())
//				jsch.addIdentity(client.identity);
			Session session=jsch.getSession(client.user, client.host, client.port);
	        session.setConfig("StrictHostKeyChecking", "no");
	        //set auth info interactively
	        //session.setUserInfo(new UserInfo(){.....});
	        session.setPassword(client.password);
	        session.connect();
	        com.jcraft.jsch.ChannelSftp fc=(com.jcraft.jsch.ChannelSftp)session.openChannel("sftp");
	        fc.connect();
	        fc.get(src,dest);
	        fc.disconnect();
	        session.disconnect();
	        return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return false;
	}
	
	static public String sshRun(SSHClient client,String cmd)
	{
		return sshRun(client,cmd,DefaultSSHCmdTimeout);
	}

	
	static public Task runAsync(String name,String cmd)
	{
		return runAsync(name,null,null,DefaultCmdTimeout,cmd);
	}
	
	static public Task runAsync(String cmd)
	{
		return runAsync(null,null,null,DefaultCmdTimeout,cmd);
	}
	
	static public Task runAsync(String name,String cmd,long timeout)
	{
		return runAsync(name,null,null,timeout,cmd);
	}
	
	static public Task runAsync(String cmd,long timeout)
	{
		return runAsync(null,null,null,timeout,cmd);
	}
	
	static public boolean  hasTaskRunning()
	{
		return  instance!=null; 
	}
	
	static public int getTaskCount()
	{
		if(instance!=null)
			return instance.TaskList.size();
		return 0;
	}
	
	static public Task addTask(String name,String cmd,Process process,long timeout)
	{
		Task task=new Task();
		task.name=name;
		task.time=System.currentTimeMillis();
		task.timeout=timeout;		
		if(instance==null) 
		{
			instance=new ExeUtil();
			instance.setDaemon(true);
			instance.start();
		}
		instance.TaskList.put(process,task);
		return task;
	}

//=======非静态部分=============
	
	ConcurrentHashMap<Process,Task> TaskList=new ConcurrentHashMap<Process,Task>();
	
	public void run()
	{
		while(TaskList.size()>0)
		{
			Process[] list=TaskList.keySet().toArray(emptyProcessArray);
			for(Process process:list)
			{
				if(process.isAlive())
				{
					Task task=TaskList.get(process);
					if(System.currentTimeMillis()-task.time>=task.timeout)
					{
						TaskList.remove(process);
						process.destroyForcibly();
						System.out.println("task timeout, kill :"+task);
					}
				}
				else
				{
					Task task=TaskList.get(process);
					TaskList.remove(process);
					System.out.println("task normal quit :"+task);
				}
			}
			try
			{
				Thread.sleep(1000l);
			} 
			catch (InterruptedException e)
			{
				break;
			}
		}		
		System.out.println("task all over, monitor thread quit!");
		instance=null;
	}
}
